/*
 * btvpms: com.dota.framework.web.context.ContextHolder.java Create on Apr 29, 2008 10:15:29 AM By Q-Wang
 * $Revision: 1.2 $
 */
package com.ctvit.framework.web.context;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

import com.ctvit.framework.service.BaseService;

/**
 * 应用程序信息ContextHolder.
 * 
 * 在无法取得ServletContext,Request的地方可以通过此类取得
 * ServletContext等信息.
 * @author Q-Wang
 */
public class ContextHolder implements Serializable {

	private final Map<Object,Object> holder = new HashMap<Object,Object>();

	private ServletContext servletContext;

	private ApplicationContext applicationContext;

	private static ThreadLocal<HttpServletRequest> currentRequest = new ThreadLocal<HttpServletRequest>();

	private static ContextHolder instance = new ContextHolder();

	private Map<Class<?>, Object> beanMap = new HashMap<Class<?>, Object>();

	private Properties properties = new Properties();

	private String VERSION = "NOT DETECTED";

	//	/*
	//	 *
	//	 */
	//	private static final class LazyHolder {
	//		private static final ContextHolder instance = new ContextHolder();
	//	}

	/**
	 * 设置全局变量.
	 * 
	 * @param key 全局变量名.
	 * @param value
	 */
	public synchronized void set(Object key, Object value) {
		if (key==null||key.equals("")) {
			throw new IllegalArgumentException("key value:[null],empty and reserved keys not allowed - " + key);
		}
		holder.put(key, value);
	}

	/**
	 * 取得全局变量.
	 * 
	 * @param key
	 * @return
	 */
	public Object get(Object key) {
		return holder.get(key);
	}

	/**
	 * 设置ServletContext.
	 * 
	 * 框架用.
	 * @param servletContext
	 */
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	/**
	 * 取得ServletContext.
	 * 
	 * @return
	 */
	public ServletContext getServletContext() {
		return servletContext;
	}

	/**
	 * Web站点文件系统根.
	 * 
	 * @return
	 */
	public static String getWebRoot() {
		String webroot = instance.servletContext.getRealPath("/");
		return webroot.endsWith("/")?webroot:webroot+"/";
	}

	/**
	 * 取得Spring容器.
	 * 
	 * @return the applicationContext
	 */
	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	/**
	 * 设置Spring容器.
	 * 
	 * 框架用.
	 * @param applicationContext the applicationContext to set
	 */
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	private ContextHolder() {
		try {
			InputStream inputStream = ContextHolder.class.getResourceAsStream("/config/version.properties");
			properties.load(inputStream);

			String buildTime = properties.getProperty("build.time");
			String buildStart = properties.getProperty("build.start");
			String buildNumber = properties.getProperty("build.number");
			if (StringUtils.isNotEmpty(buildTime)||StringUtils.isNotEmpty(buildStart)||StringUtils.isNotEmpty(buildNumber)) {
				StringBuffer sb = new StringBuffer();
				sb.append("Build:").append(buildStart==null?"":buildStart);
				sb.append('~').append(buildTime==null?"":buildTime);
				sb.append("\t Number:").append(buildNumber==null?"UNKOWN":buildNumber);
				VERSION = sb.toString();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 根据类型取得Spring容器内的Bean.
	 * 
	 * @param <T>
	 * @param clazz Bean类型.
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBean(Class<T> clazz) {
		T result = (T) instance.beanMap.get(clazz);
		if (result==null) {
			synchronized (clazz) {
				if (instance.beanMap.get(clazz)==null) {
					Map<String, T> beansOfType = instance.applicationContext.getBeansOfType(clazz);
					result = beansOfType.size()==0?null:beansOfType.values().iterator().next();
					if (result!=null) {
						instance.beanMap.put(clazz, result);
					}
				}
			}
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(String beanName) {
		return (T) instance.applicationContext.getBean(beanName);
	}

	/**
	 * 根据Service类型取得Spring容器内的Service.
	 * 
	 * @param <T>
	 * @param clazz
	 * @return
	 */
	public static <T extends BaseService> T getService(Class<T> clazz) {
		return getBean(clazz);
	}

	public static ContextHolder getInstance() {
		return instance;
	}

	/**
	 * 取得当前HttpServletRequest对象.
	 * 
	 * @return
	 */
	public static HttpServletRequest getHttpRequest() {
		return currentRequest.get();
	}

	/**
	 * 设置当前HttpServletRequest.
	 * 
	 * <b>框架用.</b>
	 * @param request
	 */
	public static void setHttpRequest(HttpServletRequest request) {
		currentRequest.set(request);
	}

	/**
	 * 取得当前登录用户用户名.
	 * 
	 * @return 当前登录用户用户名,如果未登录,返回null.
	 */
	public static String getCurrentUser() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		return authentication==null||!authentication.isAuthenticated()?null:authentication.getName();
	}

	public static UserDetails getCurrentUserDetails() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		Object principal = authentication.getPrincipal();
		if (principal instanceof String) {
			return null;
			//			throw new IllegalStateException("Principal Type String not supported yet."); // unimplements;
		} else if (principal instanceof UserDetails) {
			return (UserDetails) principal;
		} else {
			throw new IllegalStateException("Principal Type '"+ principal.getClass() +"' not supported yet."); // unimplements;
		}
	}

	public static Authentication getCurrentAuthentication() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		return authentication;
	}

	public static Long getCurrentUserId() {
		return 0L;
	}

	/**
	 * 站台终止时清空ContextHolder ThreadLocal容器.
	 * 
	 * <b>框架用.
	 * 请勿在程序中使用.</b>
	 */
	public void shutdown() {
		this.applicationContext = null;
		this.servletContext = null;
		currentRequest.remove();
		SecurityContextHolder.clearContext();
		fireDestroyEvent();
		holder.clear();
		beanMap.clear();
		instance = null;
	}

	public static String getSystemVersion() {
		return instance.VERSION;
	}

	private void fireDestroyEvent() {
		for (ContextDestroyListener listener:listeners) {
			listener.contextDestroyed();
		}
	}

	public static void addDestroyListener(ContextDestroyListener listener) {
		listeners.add(listener);
	}

	private static List<ContextDestroyListener> listeners = new ArrayList<ContextDestroyListener>();
}
